import fs from "fs";
import { DEFAULT_METHOD, FileBuilder } from "./graphBuilder";
import { ArkBody, ArkClass } from "../ArkAnalyzer/src";
import { Edge } from "./Edge";

export enum LoggerType {
    FILE = 0,
    CLASS = 1,
    METHOD = 2,
    BLOCK = 3,
    STMT = 4,
    EDGE
}

export class Logger {
    private filePath = "app.log";
    private fileBuilders: FileBuilder[] = [];
    private callEdges: Edge[] = [];
    private on = false;

    public addEdges(edges: Edge[]) {
        if (this.on) {
            this.callEdges.push(...edges);
        }
    }

    public writeCallEdges() {
        if (this.on) {
            this.writeMessage(" ");
            this.writeMessage("CFG, DFG and CG related to function calls");
            this.writeEdges(this.callEdges);
        }
    }

    public setOn(on: boolean) {
        this.on = on;
    }

    constructor() {
        fs.truncateSync(this.filePath);
    }

    public addFileBuilder(fileBuilder: FileBuilder) {
        if (this.on) {
            this.fileBuilders.push(fileBuilder);
        }
    }

    public writeFileBuilders() {
        for (const fileBuilder of this.fileBuilders) {
            let fileSignature: string = fileBuilder.getArkFile().getFileSignature().toString();
            this.writeMessage(" ".repeat(LoggerType.FILE * 2) + "- " + fileSignature.substring(0, fileSignature.length - 1));

            for (const arkClass of fileBuilder.getArkFile().getClasses()) {
                this.writeClass(arkClass);
            }
            this.writeMessage(" ");
            this.writeMessage("CFG and DFG unrelated to function calls");
            this.writeEdges(fileBuilder.getEdges());
            this.writeMessage(" ");
        }
    }

    private writeEdges(edges: Edge[]) {
        for (let edge of edges){
            let message = edge.type + ": ";
            message += edge.start.toString() + " -> " + edge.end.toString();
            if (edge.reason !== "") {
                message += "  " + edge.reason;
            }
            this.writeMessage(message)
        }
    }

    private writeClass(arkClass: ArkClass) {
        this.writeMessage(" ".repeat(LoggerType.CLASS * 2) + "- " + arkClass.getName());
        for (const method of arkClass.getMethods()) {
            if (method.getSignature().getMethodSubSignature().toString() === DEFAULT_METHOD) {
                continue;  // continue if the method is generated by ArkAnalyzer.
            }

            this.writeMessage(" ".repeat(LoggerType.METHOD * 2) + "- " + method.getName());
            this.writeBody(method.getBody());
        }
    }

    private writeBody(arkBody: undefined | ArkBody) {
        if (arkBody === undefined) {
            return ;
        }

        for (const block of arkBody.getCfg().getBlocks()) {
            this.writeMessage(" ".repeat(LoggerType.BLOCK * 2) + "- basicBlock");
            for (const stmt of block.getStmts()) {
                this.writeMessage(" ".repeat(LoggerType.STMT * 2) + "| " + stmt.toString());
            }
        }
    }

    private writeMessage(message: string) {
        fs.appendFileSync(this.filePath, `${message}\n`, "utf8");
    }
}

export let logger: Logger = new Logger()
